/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.ui.cd.point;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.swing.BorderFactory;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

import org.apache.log4j.Logger;

import who.tmap.bo.Attrs;
import who.tmap.bo.ThematicMap;
import who.tmap.bo.cd.CDPointSymbols;
import who.tmap.bo.cd.CDTypes;
import who.tmap.bo.cd.CartographicDisplay;
import who.tmap.bo.cd.impl.CartographicDisplayFactory;
import who.tmap.bo.cd.point.CDPointDataDrivenEntry;
import who.tmap.bo.cd.point.CDPointDataDrivenSymbol;
import who.tmap.common.TMapException;
import who.tmap.services.ColorService;
import who.tmap.toolkit.impl.AttrHelper;
import who.tmap.toolkit.shape.annotation.impl.ImageHelper;
import who.tmap.ui.cd.JColorTableComboBoxRenderer;
import who.tmap.ui.cd.JComboBoxPattern;
import who.tmap.ui.cd.JPanelCDHelper;
import who.tmap.ui.cd.TableComboBoxRendererPattern;
import who.tmap.ui.util.JComboBoxColor;
import who.tmap.ui.util.MiscUIHelper;
import who.tmap.ui.util.jsorttable.JSortTable;
import who.tmap.ui.util.jsorttable.SortTableModel;
import who.tmap.util.MiscHelper;

/**
 * @author stl
 *
 * To change the template for this generated type comment go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
public class JPanelCDPointDataDrivenSymbol extends JPanel implements PanelCDPoint {

    private static Logger logger = Logger.getLogger(JPanelCDPointDataDrivenSymbol.class); 	
		
	private JPanel thisPanel =this;
	private ThematicMap thematicMap =null;
	private boolean dirty =false;
	
	private JPanelCDHelper jPanelCDHelper =new JPanelCDHelper();	
	private CDPointTableModel dataModel =null;
	private TableCellEditor[] colorEditors;
	private TableCellRenderer[] colorRenderers;	
	private ImageHelper[] imageHelpers;

	private JPanel jPanelInfo = null;

	private JComboBox jComboBoxDataColumn = null;

	private JScrollPane jScrollPane = null;

	private JTable jTable = null;
	
	private MiscUIHelper miscUIHelper =new MiscUIHelper();
	
	private boolean initializing =false;
		

	/**
	 * This is the default constructor
	 */
	public JPanelCDPointDataDrivenSymbol() {
		super();
		initialize();
	}
	
	/**
	 * This method initializes this
	 * 
	 * @return void
	 */
	private void initialize() {
		initializing =true;
		
		setSize( 300, 200 );
		setLayout( new BorderLayout() );
		setBorder( BorderFactory.createEmptyBorder(5,0,0,0) );
		add( getJPanelInfo(), BorderLayout.NORTH );
		add( getJScrollPane(), BorderLayout.CENTER );
		
		initializing =false;
	}
	
	public ThematicMap getThematicMap() {
		return thematicMap;
	}
	
	public void setThematicMap(ThematicMap thematicMap) {
		this.thematicMap = thematicMap;
	}
	
	public void refreshAttrColumnNames() {

	}
	
	public void refreshData() {
		if ( thematicMap.getCartographicDisplay() instanceof CDPointDataDrivenSymbol ) {
			CDPointDataDrivenSymbol cd =(CDPointDataDrivenSymbol)thematicMap.getCartographicDisplay();
			
			if ( cd != null ) {
				getJComboBoxDataColumn().setSelectedItem( cd.getDataColumn() );
				dataModel.setRows( convertEntryMapToList( cd.getEntries() ) );
				refreshTable();
				setDirty( cd.isDirty() );
			}
		}
	}	
	
	public CartographicDisplay getCartographicDisplay() throws TMapException {
		CDPointDataDrivenSymbol cd =(CDPointDataDrivenSymbol)CartographicDisplayFactory.getInstance().get( CDTypes.POINT_DATA_DRIVEN_SYMBOL );
		
		cd.setDataColumn( (String)getJComboBoxDataColumn().getSelectedItem() );		
		
		cd.setPolygonAssociatedData( null );
		
		int idx =0;
		for( CDPointDataDrivenEntry entry : dataModel.getRows() ) {
			entry.setImage( imageHelpers[ idx ].getImage() );
			entry.setImageBytes( imageHelpers[ idx ].getImageBytes() );
			
			idx++;
		}
		
		Map<String, CDPointDataDrivenEntry> entries =convertEntryListToMap( dataModel.getRows() );
		
		cd.setEntries( entries );
		
		cd.setDirty( isDirty() );
		
		return cd;
	}

	/**
	 * This method initializes jPanelInfo	
	 * 	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getJPanelInfo() {
		if (jPanelInfo == null) {
			GridLayout gridLayout = new GridLayout(1, 2, 5, 5);
			
			jPanelInfo = new JPanel();
			jPanelInfo.setBorder( BorderFactory.createEmptyBorder(0,0,5,0) );
			jPanelInfo.setLayout(gridLayout);
			
			jPanelInfo.add( new JLabel( "Data column" ) , null);
			jPanelInfo.add( getJComboBoxDataColumn(), null);
		}
		return jPanelInfo;
	}

	public boolean isDirty() {
		return dirty;
	}

	public void setDirty(boolean dirty) {
		this.dirty = dirty;
	}

	/**
	 * This method initializes jComboBoxScalingFactor	
	 * 	
	 * @return javax.swing.JComboBox	
	 */
	JComboBox getJComboBoxDataColumn() {
		if (jComboBoxDataColumn == null) {
			jComboBoxDataColumn = new JComboBox();
			jComboBoxDataColumn.addActionListener(new ActionListener() {
				public void actionPerformed( ActionEvent e ) {
					setDirty( true );
					refreshTable();
				}
			});
		}
		return jComboBoxDataColumn;
	}

	/**
	 * This method initializes jScrollPane	
	 * 	
	 * @return javax.swing.JScrollPane	
	 */
	private JScrollPane getJScrollPane() {
		if (jScrollPane == null) {
			jScrollPane = new JScrollPane();
			jScrollPane.setViewportView(getJTable());
		}
		return jScrollPane;
	}

	/**
	 * This method initializes jTable	
	 * 	
	 * @return javax.swing.JTable	
	 */
	private JTable getJTable() {
		if (jTable == null) {

			dataModel = new CDPointTableModel( CDTypes.POINT_DATA_DRIVEN_SYMBOL );	
			
			jTable =new JSortTable( (SortTableModel)dataModel ) {
	            //  Determine editor to be used by row
	            public TableCellEditor getCellEditor(int row, int column)
	            {
	                if ( column == CDPointTableModel.COLOR_COL ) {
	                    return colorEditors[ row ];
	                } else {
	                    return super.getCellEditor(row, column);
	                }
	            }
	            
	            public TableCellRenderer getCellRenderer(int row, int column)
	            {
	                if ( column == CDPointTableModel.COLOR_COL ) {
	                    return colorRenderers[ row ];
	                } else {
	                    return super.getCellRenderer(row, column);
	                }	            	
	            }
	        };
			
			TableColumn patternCol = jTable.getColumnModel().getColumn( CDPointTableModel.FILL_PATTERN_COL );
			patternCol.setCellRenderer( new TableComboBoxRendererPattern() );			
			JComboBoxPattern patternCb =new JComboBoxPattern();			
			patternCol.setCellEditor( new DefaultCellEditor( patternCb ) );	
			
			TableColumn symbolCol = jTable.getColumnModel().getColumn( CDPointTableModel.SYMBOL_COL );
			symbolCol.setCellEditor( new JComboBoxEditorCDPointSymbol( getNewJComboBoxCDPointSymbol() ) );//new JComboBox( CDPointSymbols.namesWithBitmap ) ) );	
			
			// Causes JComboBox inside JTable to fail with Java 6 !
			//jTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
			
			refreshTable();
		}
		return jTable;
	}	
	
	private JComboBox getNewJComboBoxCDPointSymbol() {
		JComboBox jComboBoxSymbol = new JComboBox( CDPointSymbols.names );
		
		jComboBoxSymbol.setSelectedIndex( CDPointSymbols.DEFAULT );
		
		jComboBoxSymbol.addActionListener( new ActionListener() {
			public void actionPerformed(ActionEvent e) {

				int rowIdx =getJTable().getSelectedRow();
				int symbolIdx =((JComboBox)(e.getSource())).getSelectedIndex();
				
				if ( rowIdx != -1 && symbolIdx == CDPointSymbols.BITMAP && !initializing ) {
					
					// We set a default image as opening/closing the file chooser triggers the repainting of the map
					Image img =new MiscHelper().getImageIcon( DEFAULT_BITMAP ).getImage();
					
					CDPointDataDrivenEntry entry =dataModel.getRows().get( rowIdx );
					if ( entry.getImage() == null ) {
						entry.setImage( img );
					}
					
					File file =miscUIHelper.openBitmapFile();
					if ( file != null ) {
						try {
							imageHelpers[ rowIdx ].loadFile( file );
							setDirty( true );
						} catch (IOException e1) {
							logger.error( e1 );
						}
					}
					
				}
			}
		});
		
		return jComboBoxSymbol;
	}	

	public void refreshTable() {
		if ( thematicMap != null ) {
			Attrs data =thematicMap.getData();
			String dataColumnName =(String)getJComboBoxDataColumn().getSelectedItem();
			if ( dataColumnName == null ) {
				return;
			}
			
			int dataColumnIdx =-1;
			try {
				dataColumnIdx =new AttrHelper().getIdxForAttrName( data.getHeader(), dataColumnName );
			} catch (TMapException e1) {
				logger.error( e1 );
			}
			
			if ( dataColumnIdx != -1 ) {
				
				List<String> dataVals =new ArrayList<String>();
				for( List<Object> row : data.getRows() ) {
					
					String dataVal =String.valueOf( row.get( dataColumnIdx ) );
					
					if ( !dataVals.contains( dataVal ) ) {
						dataVals.add( dataVal );
					}
				}

				Map<String, CDPointDataDrivenEntry> entries =convertEntryListToMap( dataModel.getRows() );				
				List<CDPointDataDrivenEntry> newList =new ArrayList<CDPointDataDrivenEntry>();
				int ct =0;
				for( String dataVal : dataVals ) {
					try {
						CDPointDataDrivenEntry entry =entries.get( dataVal );
					
						if ( entry == null ) {
							entry =(CDPointDataDrivenEntry)CartographicDisplayFactory.getInstance().get( CDTypes.POINT_DATA_DRIVEN_ENTRY );
							entry.setVal( dataVal );
							entry.setSymbol( CDPointDataDrivenEntry.DEFAULT_SYMBOL );
							entry.setFillPattern( CDPointDataDrivenEntry.DEFAULT_FILL_PATTERN );
							entry.setColor( CDPointDataDrivenEntry.DEFAULT_COLOR );
							entry.setSize( CDPointDataDrivenEntry.DEFAULT_SIZE );
						} 
						
						newList.add( entry );
						
						ct++;
					} catch (TMapException e) {
						logger.error( e );
					}
				}

				dataModel.setRows( newList );
				applyColorColumnEditors( newList );
			}
		}
	}
	
	private Map<String, CDPointDataDrivenEntry> convertEntryListToMap( List<CDPointDataDrivenEntry> entries ) {
		Map<String, CDPointDataDrivenEntry> map =new TreeMap<String, CDPointDataDrivenEntry>( String.CASE_INSENSITIVE_ORDER );

		if ( entries != null ) {
			for( CDPointDataDrivenEntry entry : entries ) {
				map.put( entry.getVal(), entry );
			}	
		}
		
		return map;
	}
	
	private List<CDPointDataDrivenEntry> convertEntryMapToList( Map<String, CDPointDataDrivenEntry> entries ) {
		List<CDPointDataDrivenEntry> list =new ArrayList<CDPointDataDrivenEntry>();
		
		for( CDPointDataDrivenEntry entry : entries.values() ) {
			list.add( entry );
		}
		
		return list;
	}
	
	private void applyColorColumnEditors( List<CDPointDataDrivenEntry> rows ) {
		if ( rows != null ) {
			colorEditors =new TableCellEditor[ rows.size() ];
			colorRenderers =new TableCellRenderer[ rows.size() ];	
			ColorService[] colorServices =new ColorService[ rows.size() ];
			
			if ( imageHelpers == null ) {
				imageHelpers =new ImageHelper[ rows.size() ];
			}
			
			for( int ct =0; ct < rows.size(); ct++ ) {
				JComboBoxColor cb =new JComboBoxColor();

				colorEditors[ ct ] =new DefaultCellEditor( cb );	
				colorRenderers[ ct ] =new JColorTableComboBoxRenderer( cb.getColorService() );
				colorServices[ ct ] =cb.getColorService();
				cb.enableItemStateChanged( false );
				cb.setSelectedColor( rows.get( ct ).getColor() );
				cb.enableItemStateChanged( true );	
			
				if ( imageHelpers[ ct ] == null ) {
					imageHelpers[ ct ] =new ImageHelper();
				}
				
				imageHelpers[ ct ].setImage( rows.get( ct ).getImage() );
				imageHelpers[ ct ].setImageBytes( rows.get( ct ).getImageBytes() );				
			}
			
			dataModel.setColorServices( colorServices );
		}
	}	
		
}
